package smooth.core.object.googleMapComponent {
	import com.google.maps.LatLng;
	import com.google.maps.LatLngBounds;
	import com.google.maps.Map;
	import com.google.maps.MapEvent;
	import com.google.maps.overlays.Marker;
	import com.google.maps.overlays.MarkerOptions;
	import com.google.maps.styles.FillStyle;
	import com.google.maps.styles.StrokeStyle;
	import flash.text.TextField;
		
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;
	
	import smooth.core.object.googleMapComponent.tuio.TouchEventsManager;
	import smooth.core.object.googleMapCanvasComponent.GoogleMapCanvasComponent
	import flash.events.TUIO;
	import flash.events.TUIOObject;
	import flash.events.TouchEvent;
	
	public class TappableMap extends Map {
		
		private var mouseDownCounter:Number = 0;
		public var blob1:Object;
		private var state:String;
		public var blob2:Object;
		private var distanceBlob1Blob2:Number;
		private var oldDistanceBlob1Blob2:Number = 0;
		private var angleBlob1Blob2:Number;
		private var centerPointBlob1Blob2:Point;
		private var zoomDistance:Number = 0;
		private var firstPos:Point=new Point();
		private var testSprite:Sprite = new Sprite();
		private var disableStaticDragging:Boolean;
		private var isFreezed:Boolean;
		
		private var STATE_DRAGGING:String = "dragging";
		private var STATE_SCALE:String = "scale";
		private var STATE_LONGPRESS:String = "longPress";
		private var STATE_NOTHING:String = "nothing";
		private var ZOOM_THRESHOLD:Number = 40;
		
		private var uniqueId:Number;
		private var markerA:Marker;
		
		public function TappableMap(disableStaticDragging:Boolean = false, isFreezed:Boolean = false) {
			super();
			this.uniqueId = Math.random();
			this.disableStaticDragging = disableStaticDragging;
			this.isFreezed = isFreezed;
			addEventListener(TouchEvent.MOUSE_DOWN, onTouchDown);
			addEventListener(MapEvent.MAP_READY, onMapReady);
			// DEBUT - LSA - 20081003
			//addEventListener(TouchEvent.LONG_PRESS, onTouchLongPress, false, 0, true);
			// FIN - LSA - 20081003
		}
		
		private function onMapReady(event:MapEvent):void {
		}
		
		protected function onTouchDown(e:TouchEvent):void {
			if (this.isFreezed)
				return;
			//add touch event to global touch array
			TouchEventsManager.getInstance().addActiveEvent(e, this);
			mouseDownCounter=mouseDownCounter+1;
			addEventListener(TouchEvent.MOUSE_UP, localMouseUp);
			addEventListener(Event.ENTER_FRAME, handleEnterFrame);
			var tmpCurPt:Point = new Point(e.stageX, e.stageY);
			var curPt:Point = this.globalToLocal(tmpCurPt);
			
			//find out if the contact was with one or with more fingers
			if(mouseDownCounter > 0){
				//we're only dragging
				if(mouseDownCounter == 1){
					state = STATE_DRAGGING;
					blob1 = {id: e.ID, origX: curPt.x, origY: curPt.y, myOrigX:x, myOrigY:y};
				}
				//we're going to scale the map
				if(mouseDownCounter > 1){
					var activeEvents:Array = TouchEventsManager.getInstance().getActiveEvents(this);
					if(activeEvents.length ==1){
						state = STATE_DRAGGING;
						blob1 = {id: e.ID, origX: curPt.x, origY: curPt.y, myOrigX: x, myOrigY:y};
						//mouseDownCounter=1;
					}else{
						try{
							if (this.disableStaticDragging) {
								blob1 = {id: e.ID, origX: curPt.x, origY: curPt.y, myOrigX: x, myOrigY:y};
							} else {
								state = STATE_SCALE;
								var tmpTUIOObject0:TUIOObject = TUIO.getObjectById(activeEvents[0].id);
								var tmpPtObject0:Point = new Point(tmpTUIOObject0.x, tmpTUIOObject0.y);
								var ptObject0:Point = this.globalToLocal(tmpPtObject0);
								var tmpTUIOObject1:TUIOObject = TUIO.getObjectById(activeEvents[1].id);
								var tmpPtObject1:Point = new Point(tmpTUIOObject1.x, tmpTUIOObject1.y);
								var ptObject1:Point = this.globalToLocal(tmpPtObject1);
								blob1 = {id:activeEvents[0].id, origX:ptObject0.x, origY:ptObject0.y};
								blob2 = {id:activeEvents[1].id, origX:ptObject1.x, origY:ptObject1.y};
							}
							
							//calculate initial distance of the blobs to see later on in handleEnterFrame if you want to 
							//zoom in (distanceBlob1Blob2 gets bigger) or out (distanceBlob1Blob2 gets smaller)
							distanceBlob1Blob2 = calculateDistance(blob1.origX, blob1.origY, blob2.origX, blob2.origY);
						}catch(error:Error){
							trace("catch error. activeEvents.length",activeEvents.length)
							state = STATE_DRAGGING;
							blob1 = {id: e.ID, origX: curPt.x, origY: curPt.y, myOrigX: x, myOrigY:y};
							//mouseDownCounter=1;
						}
					}
				}
			}
			
			//the point in the map where you tapped. you need this for panning in the map.
			if (this.disableStaticDragging) {
				firstPos.x = this.parent.parent.x;
				firstPos.y = this.parent.parent.y;
			} else {
				firstPos.x = curPt.x;
				firstPos.y = curPt.y;
			}
			// DEBUT - LSA - 20081003
			if (!disableStaticDragging) {
				e.stopPropagation();
			}
			// FIN - LSA - 20081003
		}
		
		protected function onTouchLongPress(e:TouchEvent):void {
			var selectedPoint:Point = new Point(e.localX, e.localY);
			var selectedPointLatLng:LatLng = this.fromViewportToLatLng(selectedPoint);
			var selectedLat:Number = selectedPointLatLng.lat();
			var selectedLng:Number = selectedPointLatLng.lng();
			var googleMapCanvasComponent:GoogleMapCanvasComponent = this.parent.parent as GoogleMapCanvasComponent;
			trace("#"+selectedLat+"#"+selectedLng+"#");
			googleMapCanvasComponent.doCreateMarker(selectedPoint, selectedLat, selectedLng, this);
		}
		
		//reset everything after removing your finger	    
	    public function mouseUp(e:TouchEvent):void {
			if (this.isFreezed)
				return;
			if(mouseDownCounter > 0){
				mouseDownCounter = mouseDownCounter -1;
			}
			if(mouseDownCounter == 0){
				state = STATE_NOTHING;
				//removeEventListener(TouchEvent.MOUSE_UP, localMouseUp);
				removeEventListener(Event.ENTER_FRAME, handleEnterFrame);
				TouchEventsManager.getInstance().emptyActiveEvents(this);
			}
			if(mouseDownCounter == 1){
				state = STATE_DRAGGING;
			}
			if(mouseDownCounter < 2){
				oldDistanceBlob1Blob2 = 0;
				distanceBlob1Blob2 = 0;
				zoomDistance = 0;
			}
			if (mouseDownCounter > 1) {
				if (this.disableStaticDragging) {
					state = STATE_DRAGGING;
				} else {
					state = STATE_SCALE;
				}
			}	
		}
		
		//take your touch out of the global touch array and
		//reset everything after removing your finger
		public function localMouseUp(e:TouchEvent):void {
			if (this.isFreezed)
				return;
			TouchEventsManager.getInstance().endActiveEvent(e);
			mouseUp(e);
		}
		
		//handleEnterFrame carries out the panning and zooming activities
		//it is called in every frame while you're touching the screen
		protected function handleEnterFrame(e:Event):void {
			var ptObject:Point;
			var percentPannedX:Number;
			var percentPannedY:Number;
			
			if (this.isFreezed)
				return;
			//make your own TouchEvent object in case that you want to render
			//a TouchEvent.MOUSE_UP (you will want to if you're blob has gone
			//that means you do not touch the screen anymore
			var touchEvent:TouchEvent = new TouchEvent(TouchEvent.MOUSE_UP);
			
			if (state == STATE_DRAGGING) {
				trace("dragging map");
				//get touch object of your finger
				var tuioobj : TUIOObject = TUIO.getObjectById(blob1.id);
				if (tuioobj) {
					var tmpPtObject:Point = new Point(tuioobj.x, tuioobj.y);
					//get the bounding box of the map that is currently on screen
					var bounds:LatLngBounds = getLatLngBounds();
					//calculate lat/lon coordinate distance of bounds on screen 
					var spanX:Number = bounds.getNorthWest().lng() - bounds.getSouthEast().lng();
					var spanY:Number = bounds.getNorthWest().lat() - bounds.getSouthEast().lat();
					//set new map center by adding to the map's center the lat/lon distance multiplied 
				  	//with the amount that your finger has moved on screen 
				  	var newCenterLat:Number;
					var newCenterLon:Number;
					
					if (this.disableStaticDragging) {
						ptObject = new Point(this.parent.parent.x, this.parent.parent.y);
						percentPannedX = (this.parent.parent.x - firstPos.x)/getSize().x;
						percentPannedY = (this.parent.parent.y - firstPos.y)/getSize().y;
						newCenterLat = getCenter().lat() - spanY * percentPannedY;
						newCenterLon = getCenter().lng() - spanX * percentPannedX;
					} else {
						ptObject = this.globalToLocal(tmpPtObject);
						//find out how far your finger has moved since the last frame
						percentPannedX = (ptObject.x - firstPos.x)/getSize().x;
						percentPannedY = (ptObject.y - firstPos.y)/getSize().y;
						newCenterLat = getCenter().lat() + spanY*percentPannedY;
						newCenterLon = getCenter().lng() + spanX * percentPannedX;
					}
					this.setCenter(new LatLng(newCenterLat, newCenterLon));
					this.dispatchEvent(new MapChangedEvent(this));
				  	
					//set the position of your finger in this frame as the initial position for the next
					//frame
					firstPos.x = ptObject.x;
					firstPos.y = ptObject.y;
				} else {
					//if the touch could not be found anymore you have removed your finger and a mouseUp 
					//must be rendered
					touchEvent.ID = blob1.id;
					localMouseUp(touchEvent);
				}
			}
			
			if (state == STATE_SCALE) {
				//trace(this,"STATE_SCALE");
				//get touch objects of your fingers
				var tuioobjRS : TUIOObject = TUIO.getObjectById(blob1.id);
				var tuioobj2 : TUIOObject = TUIO.getObjectById(blob2.id);
				// START - 20081117 - LSA
				//var tmpPtObjectRS:Point = new Point(tuioobjRS.x, tuioobjRS.y);
				//var ptObjectRS:Point = this.globalToLocal(tmpPtObjectRS);
				//var tmpPtObject2:Point = new Point(tuioobj2.x, tuioobj2.y);
				//var ptObject2:Point = this.globalToLocal(tmpPtObject2);
				// END - 20081116 - LSA
				if (tuioobjRS && tuioobj2) {
					// START - 20081117 - LSA
					var tmpPtObjectRS:Point = new Point(tuioobjRS.x, tuioobjRS.y);
					var ptObjectRS:Point = this.globalToLocal(tmpPtObjectRS);
					var tmpPtObject2:Point = new Point(tuioobj2.x, tuioobj2.y);
					var ptObject2:Point = this.globalToLocal(tmpPtObject2);
					// END - 20081116 - LSA
					var zoomScale:Number;
					//find out distance of your fingers in this frame
					var distanceBlob1Blob2Enter:Number = calculateDistance(ptObjectRS.x,ptObjectRS.y, ptObject2.x, ptObject2.y);
					//find out where the center of your fingers is. this is the point where you want to 
					//zoom in later!
					centerPointBlob1Blob2 = new Point(ptObjectRS.x+(ptObject2.x - ptObjectRS.x)/2, ptObjectRS.y+(ptObject2.y - ptObjectRS.y)/2);
					
					//find out how much your fingers have moved since last frame
					var percentZoomedX:Number = centerPointBlob1Blob2.x/getSize().x;
					var percentZoomedY:Number = centerPointBlob1Blob2.y/getSize().y;
					
					//get map's bounding box
					var boundsn:LatLngBounds = getLatLngBounds();
					//calculate lat/lon distances of current map coutout
					var spanXn:Number =  boundsn.getSouthEast().lng()-boundsn.getNorthWest().lng();
				  	var spanYn:Number =  boundsn.getSouthEast().lat()-boundsn.getNorthWest().lat();
				  	
					// get old center of map
					var oldCenter:LatLng = getCenter();
				  	//add the zoom amount to the maps lat/lon positions to get the new center
				  	var newCenterLonn:Number;
				  	var newCenterLatn:Number;
				  	
				  	if (!this.disableStaticDragging) {
						newCenterLonn = boundsn.getNorthWest().lng() + spanXn*percentZoomedX;
						newCenterLatn = boundsn.getNorthWest().lat() + spanYn*percentZoomedY;
						
						//this is the really tricky part:
						//google maps has only ~22 zoom steps. there is no continous zooming but 
						//you can only set one of those steps. that means you cannot set the next 
						//step after your fingers have moved a bit away from each other but you have
						//to check if they have moved a certain distance (i took ZOOM_THRESHOLD for this
						//certain distance). that means that i have to add every little bit (distanceToLastFrame) 
						//that the 
						//fingers have moved away from each other to zoomDistance in every frame. if zoomDistance
						//is eventually bigger or smaller (zoom in or out) than ZOOM_THRESHOLD i can zoom in
						//or out one step.
						var distanceToLastFrame:Number = distanceBlob1Blob2Enter-distanceBlob1Blob2;
						zoomDistance = zoomDistance + distanceToLastFrame;
						
						if(zoomDistance > 0 && zoomDistance > ZOOM_THRESHOLD){
							while (zoomDistance - ZOOM_THRESHOLD > 0) {
								this.zoomIn(new LatLng(newCenterLatn, newCenterLonn));
								this.dispatchEvent(new MapChangedEvent(this));
								zoomDistance = zoomDistance-ZOOM_THRESHOLD;
							}
						}
						
						if(zoomDistance < 0 && zoomDistance < -ZOOM_THRESHOLD){
							while(zoomDistance+ZOOM_THRESHOLD<0){
								this.zoomOut(new LatLng(newCenterLatn, newCenterLonn));
								this.dispatchEvent(new MapChangedEvent(this));
								zoomDistance = zoomDistance+ZOOM_THRESHOLD;
							}
						}
					}
					
					distanceBlob1Blob2 = distanceBlob1Blob2Enter;
				} else {
					//render mouse ups if your fingers are not on the screen anymore
					if(!tuioobjRS){
						touchEvent.ID = blob1.id;
						//localMouseUp(touchEvent);
						TouchEventsManager.getInstance().endActiveEvent(touchEvent);
					}
					if(!tuioobj2){
						touchEvent.ID = blob2.id;
						//localMouseUp(touchEvent);
						TouchEventsManager.getInstance().endActiveEvent(touchEvent);
					}
					if (!tuioobjRS) {
						mouseUp(touchEvent);
					}
					if (!tuioobj2) {
						mouseUp(touchEvent);
					}
					
				}
			}
		}
		
		//calculates the length of the vector between two points
		private function calculateDistance(blob1X:Number, blob1Y:Number, blob2X:Number, blob2Y:Number):Number {
			var initalDistanceX:Number =blob1X - blob2X;
			var initalDistanceY:Number =blob1Y - blob2Y;
			var initalDistance:Number = Math.sqrt(initalDistanceX*initalDistanceX + initalDistanceY*initalDistanceY);
			return initalDistance;
		}
		
		public function getUniqueId():Number {
			return this.uniqueId;
		}
		
		public function setFreezeStatus(isFreezed:Boolean):void {
			this.isFreezed = isFreezed;
		}
	}
}